package cate.game.pvp.eliterace;

import cate.common.table.d.GDFight;
import cate.common.table.d.GDVideoHall;
import cate.common.util.TimeTool;
import cate.game.db.facade.KvLoader;
import cate.game.db.mongo.kv.KvMongo;
import cate.game.framework.ModuleMan;
import cate.game.framework.advanced.AdvancedByteAttributes;
import cate.game.framework.advanced.AdvancedLongAttributes;
import cate.game.notice.NoticeParam;
import cate.game.play.part.FightSide;
import cate.game.play.part.PlaySide;
import cate.game.play.proce.play.PlayPO;
import cate.game.pvp.eliterace.data.*;
import cate.game.pvp.eliterace.handler.*;
import cate.game.pvp.eliterace.support.EliteRaceGMSupport;
import cate.game.pvp.eliterace.timer.EliteRaceTimerTask.Settings;
import com.google.common.collect.Lists;
import easy.java.practice.errorcode.EcResult;
import lombok.Getter;
import org.springframework.util.CollectionUtils;

import java.util.Objects;
import java.util.stream.Collectors;

import static cate.common.table.d.GDEliteRace.WHOLE_SEASON_TIME;
import static cate.game.db.mongo.kv.KvMongo.KEY_ELITE_RACE;
import static cate.game.event.eliterace.EliteRaceTimerEvent.EventType.*;
import static cate.game.pvp.eliterace.data.EliteRacePs.INDEX_NAME;
import static cate.game.pvp.eliterace.support.EliteRaceGMSupport.RESET_LOCAL_TIME_MASK;

public class EliteRaceDataManager extends ModuleMan {
    @Getter
    private EliteRaceKV data;
    @Getter
    private final EliteRaceRankRwdData rankRwdData = new EliteRaceRankRwdData();
    @Getter
    private final EliteRaceRedPacketRecordData rpRdData = new EliteRaceRedPacketRecordData();
    @Getter
    private final EliteRaceVideoData videData = new EliteRaceVideoData();
    @Getter
    private EliteRacePsOperation psOperation;
    @Getter
    private EliteRaceRecordOperation recordOperation;
    @Getter
    private EliteRaceFightOperation fightOperation;
    @Getter
    private EliteRaceRankHandler rankHandler;
    @Getter
    private EliteRaceRankRwdOperation rankRwdOperation;
    @Getter
    private EliteRaceBetOperation betOperation;
    @Getter
    private EliteRaceRedPacketOperation redPacketOperation;
    @Getter
    private EliteRaceExcelQueryOperation excelQueryOperation;
    @Getter
    private EliteRaceVideoOperation videoOperation;

    @Override
    public void initDependent() throws Exception {
        data = new KvLoader<>(game, EliteRaceKV.class, KvMongo.KEY_ELITE_RACE).getBySrvId();

        buildKVData(data);

        this.recordOperation = new BothEliteRaceRecordOperation(this.getData()::getRecordData);
        this.rankHandler = new BothEliteRaceRankHandler(
                this.getData()::getRankData,
                (srvId, uid, rank) -> game.rank.handleAdmire(uid, rank)
        );
        this.psOperation = new BothEliteRacePsOperation(
                this.getData()::getPsData,
                () -> game.pvp.pointRace.getDataManager()
                        .getRankHandler()
                        .getRankPsFlow()
                        .collect(Collectors.toList())
                        .blockOptional()
                        .orElse(null)
        );
        this.excelQueryOperation = new BothEliteRaceExcelQueryOperation(() -> game.table);
        this.fightOperation = new BothEliteRaceFightOperation(
                this::handleFight,
                this::handleReplay,
                this.excelQueryOperation::getPoints);

        this.rankRwdOperation = new BothEliteRaceRankRwdOperation(this::getRankRwdData, this::handleLamp);
        this.betOperation = new BothEliteRaceBetOperation(this.getData()::getBetData);
        this.redPacketOperation = new BothEliteRaceRedPacketOperation(this::getRpRdData);
        this.videoOperation = new BothEliteRaceVideoOperation(this::getVideData, this::handleVideo);
    }

    private void handleLamp(NoticeParam noticeParam) {
        game.notice.lampOnline(null, noticeParam.getId(), noticeParam.getParams());
    }

    private void handleVideo(EliteRaceVideo video) {
        handleVideo(video.getPlayPO());
    }

    private void handleVideo(PlayPO playPO) {
        if (playPO == null) {
            log.error("存储VIDEO出现错误");
            return;
        }
        game.videoHall.addVideo(GDVideoHall.Group.ELITE_RACE, playPO);
    }

    private void handleReplay(PlayPO playPO, EliteRaceRecord record) {
        record.setReplayId(playPO.uid);
        game.facilities.getMongo().play.save(playPO);
    }

    private EcResult<PlayPO> handleFight(PlaySide psA, PlaySide psB) {
        FightSide fsA = FightSide.build(game.fight.getPlayBuildContext(), GDFight.Team.A, psA);
        FightSide fsB = FightSide.build(game.fight.getPlayBuildContext(), GDFight.Team.B, psB);
        return game.play.startBySystem(new EliteRaceFTC(), fsA, fsB);
    }

    private void buildKVData(EliteRaceKV data) {
        if (Objects.nonNull(data.getGmSupport())) {
            EliteRaceGMSupport gmSupport = data.getGmSupport();
            AdvancedLongAttributes attributes = gmSupport.getAttributes();
            if (attributes.hasAttribute(RESET_LOCAL_TIME_MASK)) {
                if (Objects.nonNull(data.getTimerSettings())) {
                    data.getTimerSettings().clear();
                }
                attributes.removeAttribute(RESET_LOCAL_TIME_MASK);
            }
        }

        if (CollectionUtils.isEmpty(data.getTimerSettings())) {
            long firstOpenTime = TimeTool.calTimeStamp(game.status.getOpenTime(), 2);
            data.setSpec(EliteRaceSpec.of(1, (byte) 0));
            data.setOpenTime(firstOpenTime);
            data.setSeason(1);
            data.setDay(1);
            data.setCycleTimeMs(WHOLE_SEASON_TIME);
            data.setTimerSettings(Lists.newArrayList(
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1), OPEN, EliteRaceSpec.of(1, (byte) 0)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "20:55:00"), RANK_SETTLE, EliteRaceSpec.of(1, (byte) 0)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "20:59:50"), INVITATION, EliteRaceSpec.of(1, (byte) 0)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:00:00"), TRIAL_GROUP, EliteRaceSpec.of(1, (byte) 1)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:00:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 1, (byte) 1)),//竞猜
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:03:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 1, (byte) 2)),//战斗
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:05:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 1, (byte) 3)),//准备
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:05:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 2, (byte) 1)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:08:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 2, (byte) 2)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:10:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 2, (byte) 3)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:10:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 3, (byte) 1)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:13:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 3, (byte) 2)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:15:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 3, (byte) 3)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:15:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 4, (byte) 1)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:18:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 4, (byte) 2)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:20:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 4, (byte) 3)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:20:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 5, (byte) 1)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:23:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 5, (byte) 2)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:25:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 5, (byte) 3)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:25:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 6, (byte) 1)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:28:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 6, (byte) 2)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:30:00"), TRIAL, EliteRaceSpec.of(1, (byte) 1, (byte) 0, (byte) 6, (byte) 3)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:30:00"), KO_GROUP, EliteRaceSpec.of(1, (byte) 2)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:30:00"), KO, EliteRaceSpec.of(1, (byte) 2, (byte) 0, (byte) 1, (byte) 1)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:33:00"), KO, EliteRaceSpec.of(1, (byte) 2, (byte) 0, (byte) 1, (byte) 2)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:35:00"), KO, EliteRaceSpec.of(1, (byte) 2, (byte) 0, (byte) 1, (byte) 3)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:35:00"), KO, EliteRaceSpec.of(1, (byte) 2, (byte) 0, (byte) 2, (byte) 1)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:38:00"), KO, EliteRaceSpec.of(1, (byte) 2, (byte) 0, (byte) 2, (byte) 2)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:40:00"), KO, EliteRaceSpec.of(1, (byte) 2, (byte) 0, (byte) 2, (byte) 3)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:40:00"), KO, EliteRaceSpec.of(1, (byte) 2, (byte) 0, (byte) 3, (byte) 1)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:43:00"), KO, EliteRaceSpec.of(1, (byte) 2, (byte) 0, (byte) 3, (byte) 2)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:45:00"), KO, EliteRaceSpec.of(1, (byte) 2, (byte) 0, (byte) 3, (byte) 3)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:45:00"), FINAL_GROUP, EliteRaceSpec.of(1, (byte) 3)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:45:00"), FINAL, EliteRaceSpec.of(1, (byte) 3, (byte) 0, (byte) 1, (byte) 1)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:48:00"), FINAL, EliteRaceSpec.of(1, (byte) 3, (byte) 0, (byte) 1, (byte) 2)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:50:00"), FINAL, EliteRaceSpec.of(1, (byte) 3, (byte) 0, (byte) 1, (byte) 3)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:50:00"), FINAL, EliteRaceSpec.of(1, (byte) 3, (byte) 0, (byte) 2, (byte) 1)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:53:00"), FINAL, EliteRaceSpec.of(1, (byte) 3, (byte) 0, (byte) 2, (byte) 2)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:55:00"), FINAL, EliteRaceSpec.of(1, (byte) 3, (byte) 0, (byte) 2, (byte) 3)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:55:00"), RANK_SWITCH, EliteRaceSpec.of(1, (byte) 4)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 1, "21:55:00"), SHOW, EliteRaceSpec.of(1, (byte) 5)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 4), REST, EliteRaceSpec.of(1, (byte) 6)),
                    Settings.of(TimeTool.calTimeStamp(firstOpenTime, 4), SWITCH, EliteRaceSpec.of(1, (byte) 7))
            ));
        }

        if (Objects.isNull(data.getAttributes())) {
            data.setAttributes(AdvancedLongAttributes.of(0L));
        }

        if (Objects.isNull(data.getEventAttributes())) {
            data.setEventAttributes(AdvancedLongAttributes.of(0L));
        }

        if (Objects.isNull(data.getOpAttributes())) {
            data.setOpAttributes(AdvancedByteAttributes.of((byte) 0));
        }

        if (Objects.isNull(data.getPsData())) {
            data.setPsData(new EliteRacePsData());
        }

        data.getPsData().getValueList().forEach(this::buildPsIndex);

        if (Objects.isNull(data.getBetData())) {
            data.setBetData(new EliteRaceBetData());
        }

        data.getBetData().getValueList().forEach(this::buildBetIndex);

        if (Objects.isNull(data.getRecordData())) {
            data.setRecordData(new EliteRaceRecordData());
        }

        data.getRecordData().getValueList().forEach(this::buildRecordIndex);

        if (Objects.isNull(data.getRankData())) {
            data.setRankData(new EliteRaceRankData());
        }

        data.getRankData().getValueList().forEach(this::buildRankIndex);

        if (Objects.isNull(data.getSpec())) {
            data.setSpec(EliteRaceSpec.of((byte) 1, (byte) 0));
        }

        if (Objects.isNull(data.getGmSupport())) {
            data.setGmSupport(new EliteRaceGMSupport());
            EliteRaceGMSupport gmSupport = data.getGmSupport();
            gmSupport.setAttributes(AdvancedLongAttributes.of(0L));
        }
    }

    private void buildRankIndex(EliteRaceRank rank) {
        data.getRankData().addIndex(EliteRaceRank.INDEX_NAME, rank.getUid(), rank);
    }

    private void buildRecordIndex(EliteRaceRecord record) {
        data.getRecordData().addIndex(record.getSpec().toString(), record.getIndexName(), record);
    }

    private void buildBetIndex(EliteRaceBetRecord record) {
        data.getBetData().addIndex(EliteRaceBetRecord.INDEX_NAME, record.getUid(), record);
    }

    private void buildPsIndex(EliteRacePs ps) {
        data.getPsData().addIndex(INDEX_NAME, ps.getIndexKey(), ps);
    }

    public void clear() {
        this.rankRwdData.clear();
        this.rpRdData.clear();
        this.videData.clear();
        this.data.getPsData().clear();
        this.data.getBetData().clear();
        this.data.getRecordData().clear();
        this.getRpRdData().clear();
        this.getRankRwdData().clear();
    }

    @Override
    public void shutdown() {
        super.shutdown();
        save();
    }

    public void save() {
        game.db.kv.save(data);
    }
}
